package alioss

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/mjiulee/lego/utils"
	"hash"
	"io"
	"saas_sys_base/sysconfig"
	"strings"
	"time"
)

type AliOss struct {
	AccessKeyId      string //AccessKeyId
	AccessKeySecret  string //AccessKeySecret
	Host             string // host的格式为 bucketname.endpoint //为上传oss的目标地址
	CallbackUrl      string // callbackUrl为 上传回调服务器的URL，请将下面的IP和Port配置为您自己的真实信息。
	UploadDir        string // 用户上传文件时指定的前缀。
	ExpireTime       int64  //
	Endpoint         string //例："oss-cn-shenzhen.aliyuncs.com"
	EndpointInternal string //例："oss-cn-shenzhen-internal.aliyuncs.com"
	BucketName       string //例："jinhui-test-bucket"
}

func NewAliOss() *AliOss {
	aliOss := new(AliOss)
	aliOss.AccessKeyId = sysconfig.Config.Aliyun_Oss.Key_Id
	aliOss.AccessKeySecret = sysconfig.Config.Aliyun_Oss.Key_Secret

	aliOss.CallbackUrl = sysconfig.Config.Http.Domain + ":" + utils.IntToString(sysconfig.Config.Http.Port)
	aliOss.CallbackUrl = "http://120.79.33.255:10000" //TODO: 这里后续要去掉
	if sysconfig.Config.Code.Prjname == "" {
		panic("nil prjname in config ")
	}
	aliOss.UploadDir = sysconfig.Config.Code.Prjname
	if !strings.HasSuffix(aliOss.UploadDir, "/") {
		aliOss.UploadDir = aliOss.UploadDir + "/"
	}

	if sysconfig.Config.Aliyun_Oss.Endpoint == "" || sysconfig.Config.Aliyun_Oss.Endpoint_Internal == "" {
		panic("nil Endpoint or Endpoint_Internal in config ")
	}
	aliOss.Endpoint = sysconfig.Config.Aliyun_Oss.Endpoint
	aliOss.EndpointInternal = sysconfig.Config.Aliyun_Oss.Endpoint_Internal

	aliOss.BucketName = sysconfig.Config.Aliyun_Oss.Bucket
	aliOss.Host = "http://" + sysconfig.Config.Aliyun_Oss.Bucket + "." + sysconfig.Config.Aliyun_Oss.Endpoint

	aliOss.ExpireTime = 30
	return aliOss
}

const (
	base64Table = "321QRSTUabcdVWXYZHijKLAWDCABDstEFGuvwxyzGHIJklmnopqr234560178912"
)

var coder = base64.NewEncoding(base64Table)

func base64Encode(src []byte) []byte {
	return []byte(coder.EncodeToString(src))
}

func get_gmt_iso8601(expire_end int64) string {
	var tokenExpire = time.Unix(expire_end, 0).Format("2006-01-02T15:04:05Z")
	return tokenExpire
}

type ConfigStruct struct {
	Expiration string     `json:"expiration"`
	Conditions [][]string `json:"conditions"`
}

type PolicyToken struct {
	AccessKeyId string `json:"accessid"`
	Host        string `json:"host"`
	Expire      int64  `json:"expire"`
	Signature   string `json:"signature"`
	Policy      string `json:"policy"`
	Directory   string `json:"dir"`
	Callback    string `json:"callback"`
}

type CallbackParam struct {
	CallbackUrl      string `json:"callbackUrl,omitempty"`
	CallbackBody     string `json:"callbackBody,omitempty"`
	CallbackBodyType string `json:"callbackBodyType,omitempty"`
}

type CallbackBodyDefaultStru struct {
	Filename string `json:"filename"`
	Size     string `json:"size"`
	MimeType string `json:"mimeType"`
	Height   string `json:"height"`
	Width    string `json:"width"`
}

func (t *AliOss) GetPolicyToken(callbackParam *CallbackParam) (*PolicyToken, error) {
	if callbackParam.CallbackBodyType != "application/x-www-form-urlencoded" {
		return nil, fmt.Errorf("not suport callback body type <%s>", callbackParam.CallbackBodyType) //不支持
	}
	now := time.Now().Unix()
	expire_end := now + t.ExpireTime
	var tokenExpire = get_gmt_iso8601(expire_end)

	//create post policy json
	var config ConfigStruct
	config.Expiration = tokenExpire
	var condition []string
	condition = append(condition, "starts-with")
	condition = append(condition, "$key")
	//设置上传目录
	//上传的目录由服务端指定， 每个客户端只能上传到指定的目录，实现安全隔离。
	//下面的代码以PHP为例，将上传目录改成abc/，注意目录必须以正斜线（/）结尾。
	//$dir ='abc/';
	//uploadDir := (t.UploadDir + GenDir())
	condition = append(condition, t.UploadDir)
	config.Conditions = append(config.Conditions, condition)

	//calucate signature
	result, err := json.Marshal(config)
	debyte := base64.StdEncoding.EncodeToString(result)
	h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(t.AccessKeySecret))
	io.WriteString(h, debyte)
	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))

	callbackBody := "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"
	callbackParam.CallbackUrl = t.CallbackUrl + callbackParam.CallbackUrl //前面的t.CallbackUrl代表host， 后面的CallbackUrl代表path， 例如"/api/mod/oss/alioss/oss_callback"
	callbackParam.CallbackBody = callbackBody
	if callbackParam.CallbackBody != "" {
		callbackParam.CallbackBody = callbackBody + "&" + callbackParam.CallbackBody
	}

	callbackParam.CallbackBodyType = "application/x-www-form-urlencoded"
	//callbackParam.CallbackBody = "{\"filename\":${object},\"size\":${size},\"mimeType\":${mimeType},\"height\":${imageInfo.height},\"width\":${imageInfo.width}}"
	//callbackParam.CallbackBodyType = "application/json"
	//'{"filename":${object},"size":"","mimeType":"","height":"","width":""}'
	callback_str, err := json.Marshal(callbackParam)
	if err != nil {
		fmt.Println("callback json err:", err)
	}
	callbackBase64 := base64.StdEncoding.EncodeToString(callback_str)

	var policyToken PolicyToken
	policyToken.AccessKeyId = t.AccessKeyId
	policyToken.Host = t.Host
	policyToken.Expire = expire_end
	policyToken.Signature = string(signedStr)
	policyToken.Directory = t.UploadDir
	policyToken.Policy = string(debyte)
	policyToken.Callback = string(callbackBase64)

	return &policyToken, nil
}

func GenDir() string {
	now := time.Now().Format("20060102")
	//timestamp := string(utils.RandString(32, 3))

	dir := (now + "/")
	return dir
}